/*
 * Copyright (C) 2025 Tsingteng MicroSystem
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "nfc_osi_api.h"
#include "nfc_log_api.h"

/**
 * @file osi_allocator.c
 * @brief 内存分配相关的API实现
*/

#ifdef OSI_ALLOCATOR_DEBUG
typedef struct tMEM_RECORD {
    /** @brief 内存块指针 */
    void *ptr;
    /** @brief 内存块大小 */
    size_t size;
    /** @brief 调用方文件名 */
    const char *file;
    /** @brief 调用方函数名 */
    const char *func;
    /** @brief 调用方文件所在行号 */
    int line;
    /** @brief 下一个内存块 */
    struct tMEM_RECORD* next;
} tMEM_RECORD;

static tMEM_RECORD *s_blocks = NULL;
tMUTEX s_rec_mutex = {
    .name = "s_rec_mutex",
    .state = MUTEX_STATE_STATIC,
    .is_recursive = false,
    .mutex = PTHREAD_MUTEX_INITIALIZER,
    .cond = PTHREAD_COND_INITIALIZER,
};

/**
 * @brief 记录内存分配情况
 *
 * @param ptr 分配的内存指针
 * @param size 分配的内存大小
 */
static void record_memory_allocation(void *ptr, size_t size, const char *file, const char *func, int line)
{
    if (osi_mutex_lock(&s_rec_mutex) != NFC_STATUS_OK) {
        NFCLOG_E("Mutex lock failed!!!");
    }
    tMEM_RECORD *block = (tMEM_RECORD*)calloc(1, sizeof(tMEM_RECORD));
    if (!block) {
        NFCLOG_E("Memory record allocation failed!!!");
        return;
    }
    block->ptr  = ptr;
    block->size = size;
    block->file = file;
    block->func = func;
    block->line = line;
    block->next = s_blocks;
    s_blocks    = block;
    if (osi_mutex_unlock(&s_rec_mutex) != 0) {
        NFCLOG_E("Mutex unlock failed!!!");
    }
}

/**
 * @brief 记录内存释放
 *
 * @param ptr 要释放的内存块指针
 */
static void record_memory_free(void *ptr)
{
    if (osi_mutex_lock(&s_rec_mutex) != NFC_STATUS_OK) {
        NFCLOG_E("Mutex lock failed!!!");
    }
    tMEM_RECORD *previous = NULL;
    tMEM_RECORD *current = s_blocks;
    while (current && current->ptr != ptr) {
        previous = current;
        current = current->next;
    }
    if (current) {
        if (previous) {
            previous->next = current->next;
        } else {
            s_blocks = current->next;
        }
        free(current);
    } else {
        NFCLOG_E("No matched ptr to free record!!!");
    }
    if (osi_mutex_unlock(&s_rec_mutex) != 0) {
        NFCLOG_E("Mutex unlock failed!!!");
    }
}

/**
 * @brief 检查内存块使用是否异常
 *
 * @param total_leak 一个获取总内存泄漏大小信息的指针
 * @param is_dump    dump打印控制开关
 * @return 无异常返回true，检测到异常则返回false
*/
static bool osi_memory_check(size_t *total_leak, bool is_dump)
{
    size_t leak_count = 0;
    size_t error_count = 0;
    size_t leak_size = 0;
    size_t error_size = 0;
    tMEM_RECORD* next = NULL;
    if (osi_mutex_lock(&s_rec_mutex) != NFC_STATUS_OK) {
        NFCLOG_E("Mutex lock failed!!!");
        return false;
    }
    tMEM_RECORD* current = s_blocks;
    while (current) {
        next = current->next;
        if (current->ptr) {
            leak_size += current->size;
            leak_count++;
        } else {
            error_size += current->size;
            error_count++;
        }
        if (is_dump) {
            NFCLOG_E("%s(%d): %s(%s size: %zd)", current->file, current->line, current->func,
                     (current->ptr != NULL) ? "Leak" : "Error", current->size);
        }
        current = next;
    }
    if (osi_mutex_unlock(&s_rec_mutex) != 0) {
        NFCLOG_E("Mutex unlock failed!!!");
        return false;
    }
    if (is_dump && (leak_count + error_count)) {
        NFCLOG_E("\n    ****** info start ******"
                 "\n      total leak(%zd): %zd"
                 "\n      total error(%zd): %zd"
                 "\n    ******  info end  ******",
                 leak_count, leak_size, error_size, error_count);
    }
    *total_leak = leak_size;
    return (leak_count + error_count) == 0;
}
#endif

/**
 * @brief 分配所需的内存空间，并返回一个指向它的指针
 *
 * @param size 内存块的大小，单位：字节
 * @return 返回一个指针，指向已分配大小的内存。失败则返回NULL
*/
void* osi_malloc_inner(size_t size, const char *file, const char *func, int line)
{
#ifndef OSI_ALLOCATOR_DEBUG
    (void) file;
    (void) func;
    (void) line;
#endif
    if (size == 0) {
        return NULL;
    }
    if (size > OSI_MAX_ALLOC_SIZE) {
        NFCLOG_E("malloc size greater than maximum, request = %zu, max= %d", size, OSI_MAX_ALLOC_SIZE);
    }
    void *ptr = malloc(size);
#ifdef OSI_ALLOCATOR_DEBUG
    record_memory_allocation(ptr, size, file, func, line);
#endif
    return ptr;
}

/**
 * @brief 分配所需的内存空间，并设置分配的内存为零，并返回一个指向它的指针
 *
 * @param size 内存块的大小，单位：字节
 * @return 返回一个指针，指向已分配大小的内存。失败则返回NULL
*/
void* osi_calloc_inner(size_t size, const char *file, const char *func, int line)
{
#ifndef OSI_ALLOCATOR_DEBUG
    (void) file;
    (void) func;
    (void) line;
#endif
    OSI_CHK_PARAM_INVALID(size == 0, return NULL);
    void *ptr = calloc(1, size);
#ifdef OSI_ALLOCATOR_DEBUG
    record_memory_allocation(ptr, size, file, func, line);
#endif
    return ptr;
}

/**
 * @brief 释放内存空间
 *
 * @param ptr 指针指向一个要释放内存的内存块，如果传递的参数是一个空指针，则不会执行任何动作
*/
void osi_free_inner(void* ptr)
{
#ifdef OSI_ALLOCATOR_DEBUG
    record_memory_free(ptr);
#endif
    free(ptr);
}

/**
 * @brief 分配一个帧结构内存空间
 *
 * @param element_size 帧结构的负载内存块大小，单位：字节
 * @return 返回一个指向帧结构的指针，失败则返回NULL
*/
tARRAY* osi_malloc_array_inner(size_t element_size, const char *file, const char *func, int line)
{
    OSI_CHK_PARAM_INVALID(element_size == 0, return NULL);
    tARRAY *ptr = (tARRAY*)osi_malloc_inner(element_size + sizeof(tARRAY), file, func, line);
    if (ptr) {
        ptr->cap = element_size;
        ptr->len = 0;
    }
    return ptr;
}

/**
 * @brief 释放内存空间
 *
 * @param array 指针指向一个要释放的帧结构空间
*/
void osi_free_array_inner(tARRAY* array)
{
    osi_free_inner(array);
}

#ifdef OSI_ALLOCATOR_DEBUG
/**
 * @brief dump异常内存块的信息
 *
*/
void osi_memory_dump()
{
    size_t total_leak;
    osi_memory_check(&total_leak, true);
}

/**
 * @brief 获取当前内存泄漏信息
 *
 * @return 返回内存当前泄漏大小，无泄漏则为0
*/
size_t osi_get_memory_leak_info()
{
    size_t total_leak = 0;
    osi_memory_check(&total_leak, false);
    return total_leak;
}
#endif
